package com.lysj.bhp.api.controller;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.lysj.bhp.activity.domain.Activity;
import com.lysj.bhp.activity.vo.ActivityVo;
import com.lysj.bhp.announcement.domain.Announcement;
import com.lysj.bhp.announcement.vo.AnnouncementVo;
import com.lysj.bhp.api.base.ApiContent;
import com.lysj.bhp.api.dto.PageDto;
import com.lysj.bhp.article.domain.Article;
import com.lysj.bhp.article.vo.ArticleVo;
import com.lysj.bhp.system.domain.Verify;
import com.lysj.bhp.system.repository.VerifyEmailRepository;
import com.lysj.bhp.system.repository.VerifyRepository;
import com.lysj.bhp.user.domain.User;
import com.lysj.bhp.user.vo.UserVo;
import com.lysj.bhp.util.http.HttpUtil;
import com.lysj.bhp.util.random.FormatUtil;
import com.lysj.bhp.util.random.RandomUtil;
import com.lysj.bhp.util.web.ParamUtil;
import com.lysj.bhp.util.web.Resp;
import com.lysj.bhp.withdraw.domain.WithDraw;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

@RestController
@RequestMapping("/api/v2/user")
@Api(value = "ApiPublicController", tags = "公共模块,能直接访问的接口")
public class ApiPublicController extends ApiContent {

    private static Logger logger = LoggerFactory.getLogger(ApiPublicController.class);

    @Autowired
    DefaultKaptcha defaultKaptcha;
    @Autowired
    VerifyRepository verifyRepository;
    @Autowired
    VerifyEmailRepository verifyEmailRepository;

    private final long NEXT_SEND_EMAIL = 1000 * 60 * 3;// 发送邮件间隔(15分钟)
    //    private final long NEXT_SEND_EMAIL = 0;// 发送邮件间隔(用于测试)
    private static final long expires = 1000 * 60 * 3;
    private static final long LOGIN_EXPIRES_USER = 1000 * 60 * 60 * 1;// 单位:ms/登录过期时间(用户)[1小时]
    private static final long FORGET_EXPIRES_USER = 1000 * 60 * 60 * 24 * 3;// 单位:ms/登录过期时间(用户)[3天]

    //前端导航栏汇率信息
    private static long EXPRIES = 1000 * 60 * 5;// 汇率过期时间5分钟
    private static Map<String, String> RESP_MAP = new HashMap<>();
    private static long RESP_MAP_EXPRIES = 0L;
    private static String BCEX_BTC_TO_CNY_URL="http://120.77.85.235:8098/bcex/btcCny";
    private static String BCEX_MARKETS_URL="http://120.77.85.235:8098/bcex/markets";

    //  登录相关接口
    @ApiOperation(value = "用户登录", notes = "1.需要邮箱和密码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "email", value = "用户邮箱", required = true, paramType = "form", dataType = "string"),
            @ApiImplicitParam(name = "password", value = "用户密码", required = true, paramType = "form", dataType = "string")
    })
    @PostMapping(value = "/login")
    public Resp login(User model) throws IOException {
        if (ParamUtil.isBlack(model.getEmail())) {
            return new Resp(Resp.PARAM_ERROR, "请输入邮箱地址");
        }
        if (ParamUtil.isBlack(model.getPassword())) {
            return new Resp(Resp.PARAM_ERROR, "请输入密码");
        }
        User userQuery = userRepository.findByEmail(model.getEmail());
        if (userQuery == null) {
            return new Resp(Resp.PARAM_ERROR, "用户名或者密码错误!");
        }
        if (!userQuery.getPassword().equalsIgnoreCase(ParamUtil.md5(model.getPassword()))) {
            return new Resp(Resp.PARAM_ERROR, "用户名或者密码错误!");
        }
        if (userQuery.getStatus() == 0) {
            return new Resp(Resp.PARAM_ERROR, "用户还未激活");
        }
        userQuery.setLoginIp(request.getRemoteAddr());
        userQuery.setLoginDate(new Date());
        if (userQuery.getExpires() < System.currentTimeMillis()) {// token不在有效期内,更新token
            userQuery.setToken(ParamUtil.uuid());
        }
        userQuery.setExpires(System.currentTimeMillis() + LOGIN_EXPIRES_USER);// 过期时间
        userRepository.save(userQuery);
        HashMap<String, Object> map = new HashMap<>();
        map.put("userQuery", userQuery);
        map.put("seting", userQuery.getTradePassword() == null ? false : true);
        return new Resp(map);
    }

    @ApiOperation(value = "用户注册", notes = "1.需要邮箱和密码;2前端自行验证确定密码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "email", value = "用户邮箱,用于登录的账号", required = true, paramType = "form", dataType = "string"),
            @ApiImplicitParam(name = "password", value = "用户密码", required = true, paramType = "form", dataType = "string")
    })
    @PostMapping(value = "/regist")
    public Resp regist(User model, String verify) throws IOException {
        if (ParamUtil.isBlack(model.getEmail())) {
            return new Resp(Resp.PARAM_ERROR, "请输入邮箱地址");
        }
        if (ParamUtil.isBlack(model.getPassword())) {
            return new Resp(Resp.PARAM_ERROR, "请输入密码");
        }
        Resp x = verifyCode(model.getEmail(), verify);
        if (x != null) return x;

        User userQuery = userRepository.findByEmail(model.getEmail());
        if (userQuery != null) {
            // 新增用户未激活重新发送邮件的功能
            if (userQuery.getStatus() == 0) { // 已注册,但是未激活
                if (System.currentTimeMillis() < userQuery.getNextSendEmailMs()) {
                    return new Resp(Resp.PARAM_ERROR, "您的操作太频繁,请稍后再进行操作");
                }
                userQuery.setNextSendEmailMs(System.currentTimeMillis() + NEXT_SEND_EMAIL);
                mailService.sendRegistSuccessHtmlMail(userQuery.getEmail(), userQuery.getActiveCode()); //重复发送邮箱给用户
                userRepository.save(userQuery);
                return new Resp(Resp.PARAM_ERROR, "您的邮箱已存在,但是没有激活,请登录到您的邮箱激活");
            } else {
                return new Resp(Resp.PARAM_ERROR, "该邮箱已经存在,请不要重复注册!");
            }
        }
        User user = new User();
        user.setEmail(model.getEmail());
        user.setPassword(ParamUtil.md5(model.getPassword()));
        user.setRegisterDate(new Date());
        user.setLoginIp(request.getRemoteAddr());
        user.setActiveCode(ParamUtil.uuid());// 激活码
        user.setNextSendEmailMs(System.currentTimeMillis() + NEXT_SEND_EMAIL); // 设置下次发送邮件的时间
        user.setDividend(new BigDecimal(BigInteger.ZERO)); //设置用户利息(eth为单位)

        user.setBtcDividend(new BigDecimal(BigInteger.ZERO)); //设置用户利息(btc为单位)

        user.setBhpc(new BigDecimal(BigInteger.ZERO));
        user.setEthereumBalance(new BigDecimal(BigInteger.ZERO));
        user.setBitcoinBalance(new BigDecimal(BigInteger.ZERO));
        user.setBhpcAward(new BigDecimal(BigInteger.ZERO));
        boolean result = mailService.sendRegistSuccessHtmlMail(user.getEmail(), user.getActiveCode());
        if (!result) {
            return new Resp(Resp.PARAM_ERROR, "邮件发送失败,请重试!");
        }
        userRepository.save(user);
        return new Resp("注册成功");
    }

    @ApiOperation(value = "根据token查询该token是否有效", notes = "查询数据库中某个的学生信息")
    @ApiImplicitParam(name = "token", value = "用户成功登陆后获取到的Token", required = true, paramType = "query", dataType = "String")
    @PostMapping(value = "/is_login")
    public Resp isLogin(String token) {
        if (ParamUtil.isBlack(token)) {
            return new Resp(Resp.TOKEN_ERROR, "登录信息已经失效,请重新登录");
        }
        User user = userRepository.findByToken(token);
//        System.out.println(user);
        if (user == null) {
            return new Resp(Resp.TOKEN_ERROR, "登录信息已经失效,请重新登录");
        }
        if (user.getExpires() < System.currentTimeMillis()) {
            return new Resp(Resp.TOKEN_ERROR, "登录信息已经失效,请重新登录");
        }
        user.setExpires(System.currentTimeMillis() + LOGIN_EXPIRES_USER);// 登录有效期
        userRepository.save(user);
        HashMap<String, Object> map = new HashMap<>();
        map.put("userQuery", user);
        map.put("seting", user.getTradePassword() == null ? false : true);
        //判断用户是否认证
        int type = 0;
        if (user.getPassportNum() != null) {
            type = 2;
        }
        if (user.getIdNum() != null) {
            type = 1;
        }
        if (user.getIdNum() != null && user.getPassportNum() != null) {
            type = 3;
        }
        map.put("certification", type);
        return new Resp(map);
    }


    @ApiOperation(value = "退出登录", notes = "将当前用户的登录状态设置为已过期")
    @ApiImplicitParam(name = "token", value = "用户成功登陆后获取到的Token", required = true, paramType = "query", dataType = "String")
    @PostMapping(value = "/quit")
    public Resp quit(String token) {

        if (ParamUtil.isBlack(token)) {
            return new Resp("退出成功!");
        }
        User user = userRepository.findByToken(token);
        if (user != null) {
            user.setExpires(System.currentTimeMillis());
        }
        return new Resp("退出成功!");
    }

    //  密码相关接口
    @ApiOperation(value = "忘记密码_获取邮箱验证码", notes = "忘记密码_获取邮箱验证码")
    @ApiImplicitParam(name = "email", value = "用户的邮箱", required = true, paramType = "query", dataType = "String")
    @PostMapping(value = "/forget_password")
    public Resp forgetPassword(String email, String verify) {
        if (ParamUtil.isBlack(email)) {
            return new Resp(Resp.PARAM_ERROR, "邮箱地址错误!");
        }

        Resp x = verifyCode(email, verify);
        if (x != null) return x;

        User user = userRepository.findByEmail(email);
        if (user == null) {
            return new Resp(Resp.PARAM_ERROR, "邮箱地址错误!");
        }
        if (System.currentTimeMillis() < user.getNextSendEmailMs()) {
            return new Resp(Resp.PARAM_ERROR, "您的操作太频繁,请稍后再进行操作");
        }
        user.setForgetCode(ParamUtil.uuid());
        user.setForgetExpires(System.currentTimeMillis() + FORGET_EXPIRES_USER);
        boolean result = mailService.sendForgetPasswordHtmlMail(user.getEmail(), user.getForgetCode());
        if (!result) {
            return new Resp(Resp.INNER_ERROR, "邮件发送失败,请重试!");
        }
        user.setNextSendEmailMs(System.currentTimeMillis() + NEXT_SEND_EMAIL); // 设置下次发送邮件的时间
        userRepository.save(user);
        return new Resp("请登录到您的邮箱进行密码重置操作,有效期3天!");
    }

    private Resp verifyCode(String email, String verify) {
        if (ParamUtil.isBlack(verify)) {
            return new Resp(Resp.PARAM_ERROR, "验证码错误");
        }
        Verify verifyQuery = verifyRepository.findOne(email);
        if (verifyQuery == null) {
            return new Resp(Resp.PARAM_ERROR, "验证码错误");
        }
        if (!verify.equalsIgnoreCase(verifyQuery.getVerify())) {
            return new Resp(Resp.PARAM_ERROR, "验证码错误");
        }
        if (verifyQuery.getExpires() < System.currentTimeMillis()) {
            return new Resp(Resp.PARAM_ERROR, "验证码已失效");
        }
        verifyQuery.setExpires(System.currentTimeMillis());
        verifyRepository.save(verifyQuery);
        return null;
    }

    @ApiOperation(value = "重置密码", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "forgetCode", value = "操作码", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "password", value = "新的密码", required = true, paramType = "query", dataType = "String")
    })
    @PostMapping(value = "/reset_password")
    public Resp resetPassword(String forgetCode, String password) {
        if (ParamUtil.isBlack(forgetCode)) {
            return new Resp(Resp.PARAM_ERROR, "操作有误!");
        }
        User user = userRepository.findByForgetCode(forgetCode);
        if (user == null) {
            return new Resp(Resp.PARAM_ERROR, "操作有误!");
        }
        if (user.getForgetExpires() < System.currentTimeMillis()) {
            return new Resp(Resp.PARAM_ERROR, "该链接已失效,请您重新进行重置密码操作!");
        }
        if (ParamUtil.isBlack(password)) {
            return new Resp(Resp.PARAM_ERROR, "请输入密码!");
        }
        user.setPassword(ParamUtil.md5(password));
        user.setForgetExpires(System.currentTimeMillis()); // 使验证链接失效
        userRepository.save(user);

        return new Resp("重置密码成功,请重新登录!");
    }

    /**
     * @author wtl
     * @date 2018-03-08 17:13
     * @Description 注册后通过邮箱点击激活用户
     */
    @ApiOperation(value = "激活用户", notes = "注册用户，需要点击邮件激活")
    @ApiImplicitParam(name = "activeCode", value = "激活码", required = true, paramType = "query", dataType = "String")
    @PostMapping(value = "/active")
    public Resp active(String activeCode) {
        if (ParamUtil.isBlack(activeCode)) {
            return new Resp(Resp.PARAM_ERROR, "参数错误");
        }
        User user = userRepository.findByActiveCode(activeCode);
        if (user == null) {
            return new Resp(Resp.PARAM_ERROR, "激活码错误");
        }
        if (user.getStatus() == 1) {
            return new Resp(Resp.PARAM_ERROR, "该用户已经激活");
        }
        user.setLoginDate(new Date());
        user.setToken(ParamUtil.uuid());
        user.setExpires(System.currentTimeMillis() + LOGIN_EXPIRES_USER);// 过期时间
        // 激活用户
        user.setStatus(1);
        int random = RandomUtil.getRandomNum(2, 5);
//        user.setBhpcAward(random);
        userRepository.save(user);

        return new Resp(user);
    }


    @ApiOperation(value = "获取图片验证码")
    @GetMapping("/verify")
    public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String email) throws Exception {
        if (ParamUtil.isBlack(email)) {
            return;
        }
        if (!ipRecordService.verifyIp(request.getRemoteAddr())) { // 被拦截
            return;
        }

        Verify verify = new Verify();
        verify.setEmail(email);
        verify.setExpires(System.currentTimeMillis() + expires);

        byte[] captchaChallengeAsJpeg = null;
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
            //生产验证码字符串并保存到session中
            String createText = defaultKaptcha.createText();
            verify.setVerify(createText);
            System.out.println(createText);
//            httpServletRequest.getSession().setAttribute("vrifyCode", createText);
            //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
            BufferedImage challenge = defaultKaptcha.createImage(createText);
            ImageIO.write(challenge, "jpg", jpegOutputStream);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        verifyRepository.save(verify);

        //定义response输出类型为image/jpeg类型，使用response输出流输出图片的byte数组
        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");

        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaChallengeAsJpeg);
        responseOutputStream.flush();
        responseOutputStream.close();
    }


    /**
     * @author drj
     * @date 2018/06/04 13:19
     * @Description:获取导航栏,涨跌信息
     */
    @ApiOperation(value = "获取导航栏,涨跌信息")
    @PostMapping("/bcex")
    public Resp bcex() throws Exception {
        long min=1000*60*60*5;
        long max=1000*60*60*10;
        long bcexExpiresRandom=getRandomNum(min,max);
        if (System.currentTimeMillis() < RESP_MAP_EXPRIES) {
            return new Resp(Resp.SUCCESS, "success", RESP_MAP);
        }
        String btc2cny = HttpUtil.sendGet(BCEX_BTC_TO_CNY_URL,"");
        if (ParamUtil.isBlack(btc2cny)) {
            return new Resp(Resp.SUCCESS, "success", RESP_MAP);
        }
        String markets = HttpUtil.sendGet(BCEX_MARKETS_URL,"");
        if (ParamUtil.isBlack(markets)) {
            return new Resp(Resp.SUCCESS, "success", RESP_MAP);
        }
        Map<String, String> respMap = new HashMap<>();
        respMap.put("btc2cny", btc2cny);
        respMap.put("markets", markets);
        RESP_MAP = respMap;
        RESP_MAP_EXPRIES = System.currentTimeMillis() + bcexExpiresRandom;
        logger.info("bcex更新了数据:" + respMap);
        return new Resp(Resp.SUCCESS, "success", RESP_MAP);
    }


    @ApiOperation(value = "获取所有公告信息")
    @GetMapping("/get_all_announce")
    public Resp getAllAnnounce(PageDto model) {

        if (ParamUtil.isBlack(model.getLimit())) {
            model.setLimit(10);
        }
        if (ParamUtil.isBlack(model.getPage())) {
            model.setPage(0);
        }
        model.setIsExist(1);
        Pageable pageable = new PageRequest(model.getPage(), model.getLimit(), new Sort(Sort.Direction.DESC, "date"));
        Specification specification = ParamUtil.createSpecification(model);
        Page<Announcement> announcementPage = announcementRepository.findAll(specification, pageable);
        Map<String, Object> respMap = new HashMap();
        respMap.put("totalpage", announcementPage.getTotalPages());
        respMap.put("rows", announcementPage.getContent());
        return new Resp(respMap);
    }

    @ApiOperation(value = "获取所有文章信息")
    @GetMapping("/get_all_article")
    public Resp getAllArticle(PageDto model) {

        if (ParamUtil.isBlack(model.getLimit())) {
            model.setLimit(4);
        }
        if (ParamUtil.isBlack(model.getPage())) {
            model.setPage(0);
        }
        model.setIsExist(1);
        Pageable pageable = new PageRequest(model.getPage(), model.getLimit(), new Sort(Sort.Direction.DESC, "date"));
        Specification specification = ParamUtil.createSpecification(model);
        Page<Article> announcementPage = articleRepository.findAll(specification, pageable);
        Map<String, Object> respMap = new HashMap();
        respMap.put("totalpage", announcementPage.getTotalPages());
        respMap.put("rows", announcementPage.getContent());
        return new Resp(respMap);
    }


    /**
     * @author drj
     * @date 2018/5/17 16:51
     * @Description 显示文章详情, type=1代表显示中文内容,type=2代表显示英文内容
     */
    @ApiOperation(value = "获取文章详情,type=1显示中文活动信息,type=2显示英文信息")
    @GetMapping("/show_article_detail")
    public Resp showArticleDetail(Long articleId, int type) {
        if (ParamUtil.isBlack(articleId)) {
            return new Resp(Resp.PARAM_ERROR, "请选择文章");
        }
        Article article = articleRepository.findOne(articleId);
        if (null == article) {
            return new Resp(Resp.PARAM_ERROR, "查无此文章");
        }
        ArticleVo articleVo = new ArticleVo();
        if (1 == type) {
            articleVo.setContent(article.getChineseContent());
        } else {
            articleVo.setContent(article.getEnglishContent());
        }
//        articleVo.setDate(article.getDate());
        return new Resp(articleVo);
    }

    /**
     * @author drj
     * @date 2018/5/17 14:51
     * @Description 显示公告详情, type=1代表显示中文内容,type=2代表显示英文内容
     */
    @ApiOperation(value = "获取公告详情,type=1显示中文活动信息,type=2显示英文信息")
    @GetMapping("/show_announcement_detail")
    public Resp showAnnouncementDetail(Long announcementId, int type) {
        if (ParamUtil.isBlack(announcementId)) {
            return new Resp(Resp.PARAM_ERROR, "请选择公告");
        }
        Announcement announcement = announcementRepository.findOne(announcementId);
        if (null == announcement) {
            return new Resp(Resp.PARAM_ERROR, "查无此公告");
        }
        AnnouncementVo announcementVo = new AnnouncementVo();
        if (1 == type) {
            announcementVo.setContent(announcement.getChineseContent());
        } else {
            announcementVo.setContent(announcement.getEnglishContent());
        }
        return new Resp(announcementVo);
    }

    @ApiOperation(value = "获取所有活动")
    @GetMapping("/activity_list")
    public Resp list(Activity model) {
        model.setIsExist(1);
        model.setStatus(2);
        Specification specification = ParamUtil.createSpecification(model);
        List<Activity> newActivityList = activityRepository.findAll(specification, new Sort(Sort.Direction.DESC, "createDate"));
        model.setStatus(3);
        List<Activity> oldActivityList = activityRepository.findAll(specification, new Sort(Sort.Direction.DESC, "createDate"));

        Map<String, Object> respMap = new HashMap<>();
        respMap.put("newActivityList", newActivityList);
        respMap.put("oldActivityList", oldActivityList);
        return new Resp(respMap);
    }


    /**
     * @author drj
     * @date 2018/05/17 14:45
     * @Description:获取活动详情,type=1显示中文活动信息,type=2显示英文信息
     */
    @ApiOperation(value = "获取活动详情,type=1显示中文活动信息,type=2显示英文信息")
    @GetMapping("/show_activity_detail")
    public Resp showActivityDetail(Long activityId, int type) {
        if (ParamUtil.isBlack(activityId)) {
            return new Resp(Resp.PARAM_ERROR, "请选择活动");
        }
        Activity activity = activityRepository.findOne(activityId);
        if (null == activity) {
            return new Resp(Resp.PARAM_ERROR, "查无此活动");
        }
        ActivityVo activityVo = new ActivityVo();
        if (1 == type) {
            activityVo.setDetail(activity.getChineseDetail());
        } else {
            activityVo.setDetail(activity.getEnglishDetail());
        }
        return new Resp(activityVo);
    }


    @GetMapping("/users/{createDate}")
    public Resp users(@PathVariable(required = false) String createDate) {
        logger.info("调用了查询用户接口,传递的参数为:" + createDate);
        Date date = new Date();
        if (!StringUtils.isEmpty(createDate)) { // 有上传日期
            try {
                date = FormatUtil.stringTodate2(createDate);
            } catch (Exception e) {
                e.printStackTrace();
                return new Resp(Resp.PARAM_ERROR, "日期格式有误");
            }
        }
        Date startDate = FormatUtil.getDayStartDay(date);
        Date endDate = FormatUtil.getDayEndDay(date);
        List<User> users = userRepository.findByRegisterDateBetweenAndStatus(startDate, endDate, 1);//
        List<UserVo> userVos = new ArrayList<>();
        users.forEach(user -> {
            UserVo userVo = new UserVo();
            userVo.setEmail(user.getEmail());
            userVos.add(userVo);
        });
        return new Resp(userVos);
    }


    public static long getRandomNum(long begin, long end) {
        return (long) ((end - begin) * 1.0 * Math.random()) + begin;
    }

}
